package net.chrisrichardson.ec2deployer.cluster

import groovy.mock.interceptor.MockFor
import net.chrisrichardson.ec2deployer.core.EC2
import net.chrisrichardson.ec2deployer.core.AWSProperties
import net.chrisrichardson.ec2deployer.core.EC2Server
import net.chrisrichardson.ec2deployer.applications.MySqlServer
import net.chrisrichardson.ec2deployer.core.AWSPropertiesMother
import net.chrisrichardson.ec2deployer.core.AwsUtilities

class DatabaseTierTest extends GroovyTestCase {

    MockFor mockEc2
    AWSProperties awsProperties
    ClusterSpec clusterSpec
    MockFor mockMySqlServer
    def masterInstance
    def slaveInstance
    MockFor mockEC2Server

    DatabaseTier databaseTier
    MockFor mockAwsUtilities

    public void setUp() {
        mockEc2 = new MockFor(EC2.class)
        mockMySqlServer = new MockFor(MySqlServer.class)
        mockEC2Server = new MockFor(EC2Server.class)

        awsProperties = AWSPropertiesMother.makeAWSProperties()

        mockAwsUtilities = new MockFor(AwsUtilities)

        clusterSpec = new ClusterSpec().tomcats(1).slaves(1).webApp('target/ptrack', "ptrack").catalinaOptsBuilder({builder, databasePrivateDnsName ->}).schema("ptrack", ["ptrack": "ptrack"])

    }

    public void testAssignHosts() {
        def ec2 = new EC2(awsProperties)
        databaseTier = new DatabaseTier(ec2, clusterSpec)
        slaveInstance = new EC2Server()
        masterInstance = new EC2Server()
        def hosts = new ClusterHosts(mySqlSlaves: [slaveInstance], databaseServer: masterInstance)
        databaseTier.assignHosts(hosts)
        assertEquals masterInstance, databaseTier.databaseServer.host
        assertEquals 1, databaseTier.mySqlSlaves.size
        assertEquals slaveInstance, databaseTier.mySqlSlaves[0].host
    }

    public void testStart() {
        def ec2 = new EC2(awsProperties)
        databaseTier = new DatabaseTier(ec2, clusterSpec)

        mockMySqlServer.demand.configureAsMaster {}
        mockMySqlServer.demand.configureAsSlave {}
        mockMySqlServer.demand.dbCreateOnStartup{}

        mockMySqlServer.use {
            slaveInstance = new EC2Server()
            masterInstance = new EC2Server()
            def hosts = new ClusterHosts(mySqlSlaves: [slaveInstance], databaseServer: masterInstance)
            databaseTier.assignHosts(hosts)
            databaseTier.start()
        }
    }

    public void testStartWithExistingEbsVolume() {
        clusterSpec.withExistingEbsVolume("vol-123434", "/dev/sdq")

        def ec2 = new EC2(awsProperties)
        databaseTier = new DatabaseTier(ec2, clusterSpec)

        mockMySqlServer.demand.configureAsMaster {}
        mockMySqlServer.demand.configureAsSlave {}

        mockMySqlServer.use {
            slaveInstance = new EC2Server()
            masterInstance = new EC2Server()
            def hosts = new ClusterHosts(mySqlSlaves: [slaveInstance], databaseServer: masterInstance)
            databaseTier.assignHosts(hosts)
            databaseTier.start()
        }
    }

    public void testStartWithNewEbsVolume() {
        clusterSpec.withNewEbsVolume(10, "/dev/sdq")

        def ec2 = new EC2(awsProperties)
        databaseTier = new DatabaseTier(ec2, clusterSpec)

        mockMySqlServer.demand.configureAsMaster {
            clusterSpec.ebsVolume.volumeId = "vol-223"
        }
        mockMySqlServer.demand.configureAsSlave {
        }
        mockMySqlServer.demand.dbCreateOnStartup{}

        mockMySqlServer.use {
            slaveInstance = new EC2Server()
            masterInstance = new EC2Server()
            def hosts = new ClusterHosts(mySqlSlaves: [slaveInstance], databaseServer: masterInstance)
            databaseTier.assignHosts(hosts)
            databaseTier.start()
        }
    }

    public void testSaveState() {
        def ec2 = new EC2(awsProperties)
        databaseTier = new DatabaseTier(ec2, clusterSpec)

        mockMySqlServer.demand.getHost {masterInstance}
        mockMySqlServer.demand.getHost {slaveInstance}

        mockEC2Server.demand.getInstanceId() {"i-slave"}
        mockEC2Server.demand.getInstanceId() {"i-master"}

        mockEC2Server.use {
            mockMySqlServer.use {
                slaveInstance = new EC2Server()
                masterInstance = new EC2Server()
                def hosts = new ClusterHosts(mySqlSlaves: [slaveInstance], databaseServer: masterInstance)
                databaseTier.assignHosts(hosts)
                def state = [:]
                databaseTier.saveState(state)

                assertEquals(["i-slave"], state["mySqlSlaves"])
                assertEquals(["i-master"], state["databaseServer"])
            }
        }
    }

    public void testRestoreState() {
        def state = [databaseServer: ["i-master"], mySqlSlaves: ["i-slave"]]
        slaveInstance = new EC2Server()
        masterInstance = new EC2Server()

        def instancesById = ["i-master": masterInstance, "i-slave": slaveInstance]

        2.times {mockEc2.demand.findInstance {id -> instancesById[id]}}

        mockEc2.use {
            def ec2 = new EC2(awsProperties)
            databaseTier = new DatabaseTier(ec2, clusterSpec)
            databaseTier.restoreState(state)

            assertEquals masterInstance, databaseTier.databaseServer.host
            assertEquals 1, databaseTier.mySqlSlaves.size
            assertEquals slaveInstance, databaseTier.mySqlSlaves[0].host
        }
    }

    public void testConfigureMasterBackup() {
        mockMySqlServer.demand.configureBackup {String bucket, String objectPrefix, boolean master ->
            assertEquals "myBucket", bucket
            assertEquals "myObjectPrefix", objectPrefix
            assert master
        }
        mockMySqlServer.use {
            def ec2 = new EC2(awsProperties)
            databaseTier = new DatabaseTier(ec2, clusterSpec)
            slaveInstance = new EC2Server()
            masterInstance = new EC2Server()
            def hosts = new ClusterHosts(mySqlSlaves: [slaveInstance], databaseServer: masterInstance)
            databaseTier.assignHosts(hosts)

            databaseTier.configureMasterBackup "myBucket", "myObjectPrefix"
        }
    }

    public void testConfigureSlaveBackup() {

        mockMySqlServer.demand.getHost {
            slaveInstance
        }

        mockEC2Server.demand.getInstanceId {
          "i-334"
        }
        
        mockMySqlServer.demand.configureBackup {String bucket, String objectPrefix, boolean master ->
            assertEquals "myBucket", bucket
            assertEquals "myObjectPrefix", objectPrefix
            assert !master
        }

        mockEC2Server.use {
            mockMySqlServer.use {
                def ec2 = new EC2(awsProperties)
                databaseTier = new DatabaseTier(ec2, clusterSpec)
                slaveInstance = new EC2Server()
                masterInstance = new EC2Server()
                def hosts = new ClusterHosts(mySqlSlaves: [slaveInstance], databaseServer: masterInstance)
                databaseTier.assignHosts(hosts)

                databaseTier.configureSlaveBackup "i-334", "myBucket", "myObjectPrefix"
            }
        }
    }

    public void testStopSlave() {
        mockEC2Server.demand.stop {}

        mockEC2Server.use {
            def ec2 = new EC2(awsProperties)
            databaseTier = new DatabaseTier(ec2, clusterSpec)
            slaveInstance = new EC2Server()
            masterInstance = new EC2Server()
            def hosts = new ClusterHosts(mySqlSlaves: [slaveInstance], databaseServer: masterInstance)
            databaseTier.assignHosts(hosts)
            databaseTier.stopSlave()
            assertEquals masterInstance, databaseTier.databaseServer.host
            assertEquals 0, databaseTier.mySqlSlaves.size
        }
    }

    void testPrepareToClone_local() {

        clusterSpec.numberOfSlaves = 1

        def ec2 = new EC2(awsProperties)
        databaseTier = new DatabaseTier(ec2, clusterSpec)
        Map<String, Object> cloneState = [:]


        mockAwsUtilities.demand.mkdir { bucket -> }
        mockEC2Server.demand.ssh { cmd ->
            assertEquals "mysqldump ptrack  > /var/tmp/mysqldump.sql", cmd
        }
        mockAwsUtilities.demand.s3put { String s3Path, localPath ->
            assert s3Path.startsWith("MyAccessKey.tmp-clone-db/")
            assert s3Path.endsWith(".sql")
            assertEquals "/var/tmp/mysqldump.sql", localPath
        }
        
        mockEC2Server.use {
            slaveInstance = new EC2Server()
            masterInstance = new EC2Server()

            def hosts = new ClusterHosts(mySqlSlaves: [slaveInstance], databaseServer: masterInstance)
            databaseTier.assignHosts(hosts)

            mockAwsUtilities.use {
                databaseTier.prepareToClone(cloneState, clusterSpec)
                assert cloneState.isEmpty()
                assertEquals 1, clusterSpec.schemaSpec.scripts.size()
                String script =   clusterSpec.schemaSpec.scripts[0]
                assert script.startsWith("s3://MyAccessKey.tmp-clone-db/")
                assert script.endsWith(".sql")
                assertEquals 0, clusterSpec.numberOfSlaves
            }
        }
    }

    void testPrepareToClone_ebs() {

        clusterSpec.numberOfSlaves = 1
        clusterSpec.withExistingEbsVolume("vol-1234", "/dev/sdh")

        def ec2 = new EC2(awsProperties)
        databaseTier = new DatabaseTier(ec2, clusterSpec)
        Map<String, Object> cloneState = [:]


        mockEC2Server.demand.snapshotEbsVolume{ ebsVolume, schemaSpec->
            assertEquals "vol-1234", ebsVolume.volumeId
            "snap-5678"
        }

        mockEC2Server.use {
            slaveInstance = new EC2Server()
            masterInstance = new EC2Server()

            def hosts = new ClusterHosts(mySqlSlaves: [slaveInstance], databaseServer: masterInstance)
            databaseTier.assignHosts(hosts)

            mockAwsUtilities.use {
                databaseTier.prepareToClone(cloneState, clusterSpec)
                assert cloneState.isEmpty()
                assert clusterSpec.schemaSpec.scripts.isEmpty()
                assertEquals 0, clusterSpec.numberOfSlaves
                assertNull clusterSpec.ebsVolume.volumeId
                assertEquals "snap-5678", clusterSpec.ebsVolume.snapshotId
            }
        }
    }


    void testStartAfterCloning() {
        def ec2 = new EC2(awsProperties)
        databaseTier = new DatabaseTier(ec2, clusterSpec)
        clusterSpec.numberOfSlaves = 0

        mockMySqlServer.demand.configureAsMaster {}
        mockMySqlServer.demand.dbCreateOnStartup{}

        mockMySqlServer.use {
            mockEC2Server.use {
                masterInstance = new EC2Server()
                def hosts = new ClusterHosts(databaseServer: masterInstance)
                databaseTier.assignHosts(hosts)

                Map<String, Object> cloneState = [:]
                databaseTier.startAfterCloning cloneState
            }
        }
    }

  public void testSetMySqlRootPassword() {
      def ec2 = new EC2(awsProperties)
      databaseTier = new DatabaseTier(ec2, clusterSpec)

      mockMySqlServer.demand.setMySqlRootPassword { newPassword -> assertEquals "mynewpassword",newPassword }

      mockMySqlServer.use {
          slaveInstance = new EC2Server()
          masterInstance = new EC2Server()
          def hosts = new ClusterHosts(mySqlSlaves: [slaveInstance], databaseServer: masterInstance)
          databaseTier.assignHosts(hosts)

        databaseTier.setMySqlRootPassword "mynewpassword"

        assertEquals "mynewpassword", clusterSpec.schemaSpec.rootPassword

      }
  }


}